home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Sound / Normalize / bin / normalize-mp3 next >
Text File  |  2002-10-27  |  22KB  |  802 lines

  1. #! /gg/bin/perl -w
  2. # -*- perl -*-
  3. # Copyright (C) 1999--2002 Chris Vaill
  4. # This file is part of normalize.
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2, or (at your option)
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20.  
  21. #######################################################################
  22. # These variables may be customized for local setup
  23. #######################################################################
  24.  
  25. # %m becomes name of mp3 or ogg file
  26. # %w becomes name of temporary WAV file
  27. # %b becomes bitrate of re-encoded file, as specified by the -b option
  28. # Example: $OGGENCODE="oggenc -Q -b %b -o %m %w"
  29.  
  30. $MP3DECODE  = " -q -o %w %m";
  31. $MP3ENCODE  = " -quiet %w %m";
  32. $OGGDECODE  = " -q -d wav -f %w %m";
  33. $OGGENCODE  = "";
  34. $FLACDECODE = "";
  35. $FLACENCODE = "";
  36.  
  37. # The %w etc. substitutions should *not* be used in the following, as
  38. # this script knows about their options already.
  39. $VORBISCOMMENT = "";
  40. $METAFLAC      = "";
  41.  
  42. # change this if normalize is not on your path
  43. $NORMALIZE = "normalize";
  44.  
  45.  
  46. #######################################################################
  47. # No user serviceable parts below
  48. #######################################################################
  49.  
  50. use Fcntl;
  51.  
  52. sub usage {
  53.     print <<EOF
  54. Usage: $progname [OPTION]... [FILE]...
  55.   Normalize volume of mp3, ogg, or flac files by decoding, running
  56.   normalize, and re-encoding.  This requires as much extra disk space
  57.   as the largest file, decoded.  Note that for batch and mix mode, all
  58.   files must be decoded, so there must be enough disk space for the
  59.   decoded copies of all specified files.
  60.  
  61.   -a AMP         \\
  62.   -g ADJ          |
  63.   -n              |
  64.   -T THR          |_ These arguments are passed as arguments to normalize.
  65.   -b              |  Run "normalize --help" for more info.
  66.   -m              |
  67.   -v              |
  68.   -q             /
  69.  
  70.   --bitrate BR   Set bitrate of re-encoded file [default 128]
  71.   --tmpdir TMP   Put temporary WAV files in temp directory TMP
  72.   --notags       Do not copy ID3 or ogg tags to the output file
  73.   --force-encode Re-encode even if file is already normalized
  74.   --backup       Keep backups of original files, suffixed with '~'
  75.  
  76.   Force output format (this disables copying of comment tags):
  77.  
  78.   --ogg          Convert files to ogg, regardless of original format
  79.   --mp3          Convert files to mp3, regardless of original format
  80.   --flac         Convert files to flac, regardless of original format
  81.  
  82.   The following four options may be used to set the encoder and
  83.   decoder commands for mp3 and ogg vorbis.  \%m is expanded to the
  84.   name of the mp3 or vorbis file, \%w expands to the name of the
  85.   temporary WAV file, and \%b expands to the bitrate, as specified by
  86.   the --bitrate option.  The default values are shown in brackets
  87.   below.
  88.  
  89.   --mp3encode=X  mp3 encoder        [$MP3ENCODE]
  90.   --mp3decode=X  mp3 decoder        [$MP3DECODE]
  91.   --oggencode=X  ogg vorbis encoder [$OGGENCODE]
  92.   --oggdecode=X  ogg vorbis decoder [$OGGDECODE]
  93.  
  94.   -h             Display this help and exit.
  95.   -V             Display version information and exit.
  96.  
  97. Report bugs to <cvaill\@cs.columbia.edu>.
  98. EOF
  99. }
  100.  
  101.  
  102. # same effect as a backtick, but shell metacharacters are not expanded
  103. sub backtick_noshell {
  104.     my @args = @_;
  105.     my $retval = "";
  106.     defined(my $pid = open(BABY, "-|")) || die "Can't fork: $!, stopped";
  107.     if ($pid) {
  108.     local $SIG{INT} = 'IGNORE';
  109.     while (<BABY>) {
  110.         $retval .= $_;
  111.     }
  112.     close BABY;
  113.     } else {
  114.     exec(@args) || die "Can't exec $args[0], stopped";
  115.     }
  116.     $retval;
  117. }
  118.  
  119.  
  120. sub read_tags {
  121.     my ($fname) = @_;
  122.     my ($retval, $vorbis_tag, $id3v1_tag, $id3v2_tag, $id3v2_sz);
  123.  
  124.     if ($fname =~ /\.ogg$/i) {
  125.     $vorbis_tag = backtick_noshell($VORBISCOMMENT, $fname);
  126.     defined($vorbis_tag) || die "Can't run vorbiscomment: $!, stopped";
  127.     $retval = [ 'ogg', $vorbis_tag ];
  128.  
  129.     } elsif ($fname =~ /\.mp3$/i) {
  130.     open(IN, $fname) || die "Can't read $fname: $!, stopped";
  131.     # read ID3v2 tag, if it's there
  132.     # FIXME: doesn't work for ID3v2.4.0 appended tags
  133.     read(IN, $id3v2_tag, 3);
  134.     if ($id3v2_tag eq "ID3") {
  135.         read(IN, $id3v2_tag, 7, 3);
  136.         # figure tag size
  137.         my ($x1, $x2, $x3, $x4) = unpack("x6 C C C C", $id3v2_tag);
  138.         my $tagsz = $x1;
  139.         $tagsz <<= 7;
  140.         $tagsz += $x2;
  141.         $tagsz <<= 7;
  142.         $tagsz += $x3;
  143.         $tagsz <<= 7;
  144.         $tagsz += $x4;
  145.         read(IN, $id3v2_tag, $tagsz, 10);
  146.         $id3v2_sz = $tagsz + 10;
  147.     } else {
  148.         undef $id3v2_tag;
  149.         $id3v2_sz = 0;
  150.     }
  151.     # read ID3v1 tag, if it's there
  152.     seek(IN, -128, 2);
  153.     read(IN, $id3v1_tag, 3);
  154.     if ($id3v1_tag eq "TAG") {
  155.         read(IN, $id3v1_tag, 125, 3);
  156.     } else {
  157.         undef $id3v1_tag;
  158.     }
  159.     close(IN);
  160.  
  161.     $retval = [ 'id3', $id3v1_tag, $id3v2_tag, $id3v2_sz ];
  162.     } else {
  163.     $retval = [ 'none' ];
  164.     }
  165.  
  166.     $retval;
  167. }
  168.  
  169.  
  170. sub write_tags {
  171.     my ($fname, $tag) = @_;
  172.  
  173.     if ($fname =~ /\.ogg$/i) {
  174.     if ($tag->[0] eq 'ogg' && $tag->[1]) {
  175.         my @args = ($VORBISCOMMENT, "-a", $fname);
  176.         defined(my $pid = open(BABY, "|-"))
  177.         || die "Can't fork: $!, stopped";
  178.         if ($pid) {
  179.         local $SIG{INT} = 'IGNORE';
  180.         print BABY $tag->[1];
  181.         close BABY;
  182.         $? == 0 || die "Error running vorbiscomment, stopped";
  183.         } else {
  184.         exec(@args) || die "Can't run vorbiscomment: $!, stopped";
  185.         }
  186.  
  187.     }
  188.  
  189.     } elsif ($fname =~ /\.mp3$/i) {
  190.     if ($tag->[0] eq 'id3' && $tag->[1]) {
  191.         my $id3v1_tag = $tag->[1];
  192.         open(OUT, ">>".$fname)
  193.         || die "Can't append tag to $fname: $!, stopped";
  194.         syswrite(OUT, $id3v1_tag, 128);
  195.         close(OUT);
  196.     }
  197.     if ($tag->[0] eq 'id3' && $tag->[2]) {
  198.         my ($buf, $tmpfile);
  199.         my $id3v2_tag = $tag->[2];
  200.         my $id3v2_sz = $tag->[3];
  201.         my $n = $$;
  202.         while (1) {
  203.         $tmpfile = $tmpdir.$progname."-".$n.".tag";
  204.         if (sysopen(OUT, $tmpfile, O_WRONLY|O_CREAT|O_EXCL)) {
  205.             last;
  206.         }
  207.         $! == EEXIST || die "Can't write $tmpfile: $!, stopped";
  208.         $n++;
  209.         }
  210.         syswrite(OUT, $id3v2_tag, $id3v2_sz);
  211.         open(IN, $fname) || die "Can't read $fname: $!, stopped";
  212.         while ($ret = sysread(IN, $buf, 4096)) {
  213.         syswrite(OUT, $buf, $ret);
  214.         }
  215.         close(IN);
  216.         close(OUT);
  217.         unlink $fname;
  218.         rename($tmpfile, $fname)
  219.         || die "Can't rename temp file, leaving in $tmpfile, stopped";
  220.     }
  221.     }
  222. }
  223.  
  224. sub find_prog {
  225.     my ($prog) = @_;
  226.     my $retval = undef;
  227.     my $fullpath;
  228.  
  229.     @_ = split(/:/, $ENV{PATH});
  230.     for (@_) {
  231.     ($_ .= "/") unless (/\/$/);
  232.     $fullpath = $_.$prog;
  233.     if (-x $fullpath) {
  234.         $retval = $fullpath;
  235.         last;
  236.     }
  237.     }
  238.  
  239.     $retval;
  240. }
  241.  
  242. sub find_mp3decode {
  243.     my ($path);
  244.     $path = find_prog("madplay");
  245.     if ($path) { $path .= " -q -o %w %m"; }
  246.     unless ($path) {
  247.     $path = find_prog("mpg123");
  248.     if ($path) { $path .= " -q -w %w %m"; }
  249.     }
  250.     if ($path) { $MP3DECODE = $path; }
  251. }
  252.  
  253. sub find_mp3encode {
  254.     my ($path);
  255.     $path = find_prog("lame");
  256.     unless ($path) {
  257.     $path = find_prog("notlame");
  258.     }
  259.     if ($path) { $path .= " --quiet -h -b %b %w %m"; }
  260.     unless ($path) {
  261.     $path = find_prog("bladeenc");
  262.     if ($path) { $path .= " -quiet %w %m"; }
  263.     }
  264.     if ($path) { $MP3ENCODE = $path; }
  265. }
  266.  
  267. sub find_oggdecode {
  268.     my ($path);
  269.     $path = find_prog("oggdec");
  270.     if ($path) { $path .= " -Q -o %w %m"; }
  271.     unless ($path) {
  272.     $path = find_prog("ogg123");
  273.     if ($path) { $path .= " -q -d wav -f %w %m"; }
  274.     }
  275.     if ($path) { $OGGDECODE = $path; }
  276. }
  277.  
  278. sub find_oggencode {
  279.     my ($path);
  280.     $path = find_prog("oggenc");
  281.     if ($path) {
  282.     $path .= " -Q -b %b -o %m %w";
  283.     $OGGENCODE = $path;
  284.     }
  285. }
  286.  
  287. sub find_vorbiscomment {
  288.     my ($path);
  289.     $path = find_prog("vorbiscomment");
  290.     if ($path) { $VORBISCOMMENT = $path; }
  291. }
  292.  
  293. sub find_flacdecode {
  294.     my ($path);
  295.     $path = find_prog("flac");
  296.     if ($path) {
  297.     $path .= " -s -d -o %w %m";
  298.     $FLAC = $path;
  299.     }
  300. }
  301.  
  302. sub find_flacencode {
  303.     my ($path);
  304.     $path = find_prog("flac");
  305.     if ($path) {
  306.     $path .= " -s -o %m %w";
  307.     $FLAC = $path;
  308.     }
  309. }
  310.  
  311. sub find_metaflac {
  312.     my ($path);
  313.     $path = find_prog("metaflac");
  314.     if ($path) { $METAFLAC = $path; }
  315. }
  316.  
  317.  
  318. ($progname = $0) =~ s/.*\///;
  319. $version = "0.7.6";
  320. $nomoreoptions = 0;
  321.  
  322. # default option values
  323. @normalize_args = ($NORMALIZE, "--frontend", "-T", "0.25");
  324. $all_to_mp3 = 0;
  325. $all_to_ogg = 0;
  326. $all_to_flac = 0;
  327. $bitrate = 128;
  328. $do_copy_tags = 1;
  329. $tmpdir = "";
  330. $do_adjust = 1;
  331. $batch_mode = 0;
  332. $mix_mode = 0;
  333. $force_encode = 0;
  334. $keep_backups = 0;
  335. # we track verbosity separately for this script
  336. $verbose = 1;
  337.  
  338.  
  339. # for any helper programs that haven't been specified statically at
  340. # the top of this file, try to set them dynamically
  341. find_mp3decode     unless ($MP3DECODE);
  342. find_mp3encode     unless ($MP3ENCODE);
  343. find_oggdecode     unless ($OGGDECODE);
  344. find_oggencode     unless ($OGGENCODE);
  345. find_vorbiscomment unless ($VORBISCOMMENT);
  346. find_flacdecode    unless ($FLACDECODE);
  347. find_flacencode    unless ($FLACENCODE);
  348.  
  349. @infnames = ();
  350.  
  351. # step through arguments
  352. $nomoreoptions = 0;
  353. ARG_LOOP:
  354. while ($ARGV[0]) {
  355.     if ($ARGV[0] =~ /^-/ && !$nomoreoptions) {
  356.     $_ = $ARGV[0];
  357.  
  358.     if ($_ eq "-a" || $_ eq "--amplitude") {
  359.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  360.         push @normalize_args, "-a", $ARGV[1];
  361.         shift; shift; next ARG_LOOP;
  362.     } elsif ($_ eq "--bitrate") {
  363.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  364.         $bitrate = $ARGV[1];
  365.         shift; shift; next ARG_LOOP;
  366.     } elsif ($_ eq "-g" || $_ eq "--gain") {
  367.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  368.         push @normalize_args, "-g", $ARGV[1];
  369.         shift; shift; next ARG_LOOP;
  370.     } elsif ($_ eq "-n" || $_ eq "--no-adjust") {
  371.         push @normalize_args, "-n";
  372.         $do_adjust = 0;
  373.         shift; next ARG_LOOP;
  374.     } elsif ($_ eq "-T" || $_ eq "--adjust-threshold") {
  375.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  376.         push @normalize_args, "-T", $ARGV[1];
  377.         shift; shift; next ARG_LOOP;
  378.     } elsif ($_ eq "--fractions") {
  379.         push @normalize_args, "--fractions";
  380.         shift; next ARG_LOOP;
  381.     } elsif ($_ eq "--tmp" || $_ eq "--tmpdir") {
  382.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  383.         $tmpdir = $ARGV[1];
  384.         unless (-d $tmpdir) { print "$progname: $tmpdir: no such directory\n"; exit 1; }
  385.         if ($tmpdir !~ /\/$/) {
  386.         $tmpdir = $tmpdir."/";
  387.         }
  388.         shift; shift; next ARG_LOOP;
  389.     } elsif ($_ eq "-v" || $_ eq "--verbose") {
  390.         push @normalize_args, "-v";
  391.         $verbose = 2;
  392.         shift; next ARG_LOOP;
  393.     } elsif ($_ eq "-b" || $_ eq "--batch") {
  394.         push @normalize_args, "-b";
  395.         $batch_mode = 1;
  396.         shift; next ARG_LOOP;
  397.     } elsif ($_ eq "-m" || $_ eq "--mix") {
  398.         push @normalize_args, "-m";
  399.         $mix_mode = 1;
  400.         shift; next ARG_LOOP;
  401.     } elsif ($_ eq "-q" || $_ eq "--quiet") {
  402.         push @normalize_args, "-q";
  403.         $verbose = 0;
  404.         shift; next ARG_LOOP;
  405.     } elsif ($_ eq "--ogg") {
  406.         $all_to_ogg = 1;
  407.         $all_to_mp3 = 0;
  408.         $all_to_flac = 0;
  409.         shift; next ARG_LOOP;
  410.     } elsif ($_ eq "--mp3") {
  411.         $all_to_mp3 = 1;
  412.         $all_to_ogg = 0;
  413.         $all_to_flac = 0;
  414.         shift; next ARG_LOOP;
  415.     } elsif ($_ eq "--flac") {
  416.         $all_to_flac = 1;
  417.         $all_to_mp3 = 0;
  418.         $all_to_ogg = 0;
  419.         shift; next ARG_LOOP;
  420.     } elsif ($_ eq "--force-encode") {
  421.         $force_encode = 1;
  422.         shift; next ARG_LOOP;
  423.     } elsif ($_ eq "--backup") {
  424.         $keep_backups = 1;
  425.         shift; next ARG_LOOP;
  426.     } elsif ($_ eq "--notags" || $_ eq "--noid3") {
  427.         $do_copy_tags = 0;
  428.         shift; next ARG_LOOP;
  429.     } elsif ($_ eq "--mp3encode") {
  430.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  431.         $MP3ENCODE = $ARGV[1];
  432.         shift; shift; next ARG_LOOP;
  433.     } elsif ($_ eq "--mp3decode") {
  434.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  435.         $MP3DECODE = $ARGV[1];
  436.         shift; shift; next ARG_LOOP;
  437.     } elsif ($_ eq "--oggencode") {
  438.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  439.         $MP3ENCODE = $ARGV[1];
  440.         shift; shift; next ARG_LOOP;
  441.     } elsif ($_ eq "--oggdecode") {
  442.         if ($#ARGV < 1) { print "$progname: option $_ requires an argument\n"; exit 1; }
  443.         $OGGDECODE = $ARGV[1];
  444.         shift; shift; next ARG_LOOP;
  445.     } elsif ($_ eq "-h" || $_ eq "--help") {
  446.         usage;
  447.         exit 0;
  448.     } elsif ($_ eq "-V" || $_ eq "--version") {
  449.         print "$progname (normalize) $version\n";
  450.         exit 0;
  451.     } elsif ($_ eq "--") {
  452.         $nomoreoptions = 1;
  453.         shift; next ARG_LOOP;
  454.     } else {
  455.         print "Unrecognized option \"",$ARGV[0],"\"\n";
  456.         usage;
  457.         exit 1;
  458.     }
  459.     }
  460.  
  461.     push(@infnames, shift);
  462. }
  463.  
  464. unless (@infnames) {
  465.     print STDERR "Error: no files specified\n";
  466.     print STDERR "Usage: $progname [OPTION]... [FILE]...\n";
  467.     print STDERR "Try `$progname --help' for more information\n";
  468.     exit 0;
  469. }
  470.  
  471.  
  472. if ($batch_mode || $mix_mode) {
  473.  
  474.     #
  475.     # decode all files
  476.     #
  477.     @tmpfnames = ();
  478.     @outfnames = ();
  479.     for($i = 0; $i <= $#infnames; $i++) {
  480.     $input_file = $infnames[$i];
  481.  
  482.     $decoder = undef;
  483.     if ($input_file =~ /\.mp3$/i) {
  484.         $decoder = $MP3DECODE;
  485.     } elsif ($input_file =~ /\.ogg$/i) {
  486.         $decoder = $OGGDECODE;
  487.     } elsif ($input_file =~ /\.flac$/i) {
  488.         $decoder = $FLACDECODE;
  489.     } else {
  490.         print STDERR "$progname: $input_file has unrecognized extension\n";
  491.         print STDERR "$progname: Recognized extensions are mp3, ogg, and flac\n";
  492.     }
  493.  
  494.     unless ($decoder) {
  495.         print STDERR "$progname: $input_file: no decoder available\n";
  496.         splice(@infnames, $i, 1);
  497.         $i--;
  498.         next;
  499.     }
  500.  
  501.     # construct temporary file name
  502.     #   NOTE: There is a race condition here, similar to the C
  503.     #   tmpnam() function.  We are ignoring it.
  504.     ($filebase = $input_file) =~ s/^.*\///;
  505.     $filebase = $tmpdir.$filebase;
  506.     $n = $$;
  507.     do {
  508.         $tmp_file = $filebase.".".$n.".wav";
  509.         $n++;
  510.     } while (-e $tmp_file);
  511.     push(@tmpfnames, $tmp_file);
  512.     # construct output file name
  513.     ($filebase = $input_file) =~ s/\..*?$//;
  514.     if ($all_to_mp3) {
  515.         $output_file = $filebase.".mp3";
  516.     } elsif ($all_to_ogg) {
  517.         $output_file = $filebase.".ogg";
  518.     } else {
  519.         $output_file = $input_file;
  520.     }
  521.     push(@outfnames, $output_file);
  522.  
  523.     # construct decode command
  524.     @decode_args = split(/\s+/, $decoder);
  525.     for (@decode_args) {
  526.         s/^\%w$/$tmp_file/;
  527.         s/^\%m$/$input_file/;
  528.         s/^\%b$/$bitrate/;
  529.     }
  530.  
  531.     # save tags
  532.     $do_copy_tags && ($tagref = read_tags($input_file));
  533.     push(@tags, $tagref);
  534.  
  535.     # run decoder
  536.     $verbose > 0 && print STDERR "Decoding $input_file...\n";
  537.     if ($verbose < 2) {
  538.         open(OLDOUT, ">&STDOUT");
  539.         open(STDOUT, ">/dev/null") || die "Can't redirect stdout, stopped";
  540.     }
  541.     $ret = system(@decode_args);
  542.     if ($verbose < 2) {
  543.         close(STDOUT);
  544.         open(STDOUT, ">&OLDOUT");
  545.     }
  546.     $ret == 0 || die "Error decoding, stopped";
  547.     }
  548.  
  549.  
  550.     #
  551.     # normalize all files
  552.     #
  553.     $verbose > 0 && print STDERR "Running normalize...\n";
  554.     @args = (@normalize_args, @tmpfnames);
  555.     $adjust_needed = $force_encode;
  556.     defined($pid = open(NORM, "-|")) || die "Can't fork: $!, stopped";
  557.     if ($pid) {
  558.     local $SIG{INT} = 'IGNORE';
  559.     $dummy = 0; # suppress warnings about single use
  560.     while (<NORM>) {
  561.         if (/^ADJUST_NEEDED /) {
  562.         ($dummy, $adjust_needed_here) = split;
  563.         $adjust_needed = $adjust_needed || $adjust_needed_here;
  564.         } elsif (/^LEVEL /) {
  565.         unless ($do_adjust) {
  566.             # with -n specified, the line following a LEVEL line
  567.             # is the "level peak gain" line, so print it out
  568.             $_ = <NORM>;
  569.             print;
  570.         }
  571.         }
  572.     }
  573.     close NORM;
  574.     $? == 0 || die "Error during normalize, stopped";
  575.     } else {
  576.     exec(@args) || die "Can't run normalize: $!, stopped";
  577.     }
  578.  
  579.  
  580.     #
  581.     # re-encode all files
  582.     #
  583.     if ($do_adjust) {
  584.     for($i = 0; $i <= $#infnames; $i++) {
  585.         $input_file  = $infnames[$i];
  586.         $output_file = $outfnames[$i];
  587.         $tmp_file    = $tmpfnames[$i];
  588.         $tagref      = $tags[$i];
  589.  
  590.         # construct encode command
  591.         $encoder = undef;
  592.         if ($output_file =~ /\.mp3$/i) {
  593.         $encoder = $MP3ENCODE;
  594.         } elsif ($output_file =~ /\.ogg$/i) {
  595.         $encoder = $OGGENCODE;
  596.         } elsif ($output_file =~ /\.flac$/i) {
  597.         $encoder = $FLACENCODE;
  598.         } else {
  599.         print STDERR "$progname: $output_file has unrecognized extension\n";
  600.         print STDERR "$progname: Recognized extensions are mp3, ogg, and flac\n";
  601.         }
  602.  
  603.         unless ($encoder) {
  604.         print STDERR "$progname: $output_file: no decoder available\n";
  605.         print STDERR "$progname: leaving output in $tmp_file\n";
  606.         next;
  607.         }
  608.  
  609.         @encode_args = split(/\s+/, $encoder);
  610.         for (@encode_args) {
  611.         s/^\%w$/$tmp_file/;
  612.         s/^\%m$/$output_file/;
  613.         s/^\%b$/$bitrate/;
  614.         }
  615.  
  616.         if ($adjust_needed || $input_file ne $output_file) {
  617.         if ($keep_backups) {
  618.             rename($input_file, $input_file."~");
  619.         } else {
  620.             unlink($input_file);
  621.         }
  622.         # run encoder
  623.         $verbose > 0 && print STDERR "Re-encoding $input_file...\n";
  624.         if ($verbose < 2) {
  625.             open(OLDOUT, ">&STDOUT");
  626.             open(STDOUT, ">/dev/null")
  627.             || die "Can't redirect stdout, stopped";
  628.         }
  629.         $ret = system(@encode_args);
  630.         if ($verbose < 2) {
  631.             close(STDOUT);
  632.             open(STDOUT, ">&OLDOUT");
  633.         }
  634.         $ret == 0 || die "Error encoding, stopped";
  635.  
  636.         # restore tags, if necessary
  637.         $do_copy_tags && write_tags($output_file, $tagref);
  638.         } else {
  639.         $verbose > 0 && print "$input_file is already normalized, not re-encoding...\n";
  640.         }
  641.  
  642.         # delete temp file
  643.         unlink $tmp_file || print STDERR "Can't remove $tmp_file: $!\n";
  644.     }
  645.     }
  646.  
  647.     exit 0;
  648. }
  649.  
  650.  
  651. #
  652. # not mix or batch mode
  653. #
  654. for $input_file (@infnames) {
  655.  
  656.     $decoder = $encoder = undef;
  657.     if ($input_file =~ /\.mp3$/i) {
  658.     $decoder = $MP3DECODE; $encoder = $MP3ENCODE;
  659.     } elsif ($input_file =~ /\.ogg$/i) {
  660.     $decoder = $OGGDECODE; $encoder = $OGGENCODE;
  661.     } elsif ($input_file =~ /\.flac$/i) {
  662.     $decoder = $FLACDECODE; $encoder = $FLACENCODE;
  663.     } else {
  664.     print STDERR "$progname: $input_file has unrecognized extension\n";
  665.     print STDERR "$progname: Recognized extensions are mp3, ogg, and flac\n";
  666.     next;
  667.     }
  668.  
  669.     # construct temporary file name
  670.     #   NOTE: There is a race condition here, similar to the C
  671.     #   tmpnam() function.  We are ignoring it.
  672.     ($filebase = $input_file) =~ s{^.*/}{};
  673.     $filebase = $tmpdir.$filebase;
  674.     $n = $$;
  675.     do {
  676.     $tmp_file = $filebase.".".$n.".wav";
  677.     $n++;
  678.     } while (-e $tmp_file);
  679.     # construct output file name
  680.     #($filebase = $input_file) =~ s{(.*/)?(.*)\..*}{$2};
  681.     ($filebase = $input_file) =~ s{\..*?$}{};
  682.     if ($all_to_mp3) {
  683.     $output_file = $filebase.".mp3";
  684.     $encoder = $MP3ENCODE;
  685.     } elsif ($all_to_ogg) {
  686.     $output_file = $filebase.".ogg";
  687.     $encoder = $OGGENCODE;
  688.     } elsif ($all_to_flac) {
  689.     $output_file = $filebase.".flac";
  690.     $encoder = $FLACENCODE;
  691.     } else {
  692.     $output_file = $input_file;
  693.     }
  694.  
  695.     unless ($decoder) {
  696.     print STDERR "$progname: $input_file: no decoder available\n";
  697.     next;
  698.     }
  699.     unless ($encoder) {
  700.     print STDERR "$progname: $output_file: no encoder available\n";
  701.     next;
  702.     }
  703.  
  704.     # construct encode and decode commands
  705.     @decode_args = split(/\s+/, $decoder);
  706.     for (@decode_args) {
  707.     s/^\%w$/$tmp_file/;
  708.     s/^\%m$/$input_file/;
  709.     s/^\%b$/$bitrate/;
  710.     }
  711.     @encode_args = split(/\s+/, $encoder);
  712.     for (@encode_args) {
  713.     s/^\%w$/$tmp_file/;
  714.     s/^\%m$/$output_file/;
  715.     s/^\%b$/$bitrate/;
  716.     }
  717.  
  718.     # save tags
  719.     $do_copy_tags && ($tagref = read_tags($input_file));
  720.  
  721.  
  722.     #
  723.     # run decoder
  724.     #
  725.     $verbose > 0 && print STDERR "Decoding $input_file...\n";
  726.     if ($verbose < 2) {
  727.     open(OLDOUT, ">&STDOUT");
  728.     open(STDOUT, ">/dev/null") || die "Can't redirect stdout, stopped";
  729.     }
  730.     $ret = system(@decode_args);
  731.     if ($verbose < 2) {
  732.     close(STDOUT);
  733.     open(STDOUT, ">&OLDOUT");
  734.     }
  735.     $ret == 0 || die "Error decoding, stopped";
  736.  
  737.  
  738.     #
  739.     # run normalize
  740.     #
  741.     $verbose > 0 && print STDERR "Running normalize...\n";
  742.     @args = (@normalize_args, $tmp_file);
  743.     $adjust_needed = $force_encode;
  744.     defined($pid = open(NORM, "-|")) || die "Can't fork: $!, stopped";
  745.     if ($pid) {
  746.     local $SIG{INT} = 'IGNORE';
  747.     $dummy = 0; # suppress warnings about single use
  748.     while (<NORM>) {
  749.         if (/^ADJUST_NEEDED /) {
  750.         ($dummy, $adjust_needed_here) = split;
  751.         $adjust_needed = $adjust_needed || $adjust_needed_here;
  752.         } elsif (/^LEVEL /) {
  753.         unless ($do_adjust) {
  754.             # with -n specified, the line following a LEVEL line
  755.             # is the "level peak gain" line, so print it out
  756.             $_ = <NORM>;
  757.             print;
  758.         }
  759.         }
  760.     }
  761.     close NORM;
  762.     $? == 0 || die "Error during normalize, stopped";
  763.     } else {
  764.     exec(@args) || die "Can't run normalize: $!, stopped";
  765.     }
  766.  
  767.  
  768.     #
  769.     # run encoder, if necessary
  770.     #
  771.     if ($do_adjust) {
  772.     if ($adjust_needed || $input_file ne $output_file) {
  773.         if ($keep_backups) {
  774.         rename($input_file, $input_file."~");
  775.         } else {
  776.         unlink($input_file);
  777.         }
  778.         # run encoder
  779.         $verbose > 0 && print STDERR "Re-encoding $input_file...\n";
  780.         if ($verbose < 2) {
  781.         open(OLDOUT, ">&STDOUT");
  782.         open(STDOUT, ">/dev/null")
  783.             || die "Can't redirect stdout, stopped";
  784.         }
  785.         $ret = system(@encode_args);
  786.         if ($verbose < 2) {
  787.         close(STDOUT);
  788.         open(STDOUT, ">&OLDOUT");
  789.         }
  790.         $ret == 0 || die "Error encoding, stopped";
  791.  
  792.         # restore tags, if necessary
  793.         $do_copy_tags && write_tags($output_file, $tagref);
  794.     } else {
  795.         $verbose > 0 && print "$input_file is already normalized, not re-encoding...\n";
  796.     }
  797.     }
  798.  
  799.     # delete temp file
  800.     unlink $tmp_file || print STDERR "Can't remove $tmp_file: $!\n";
  801. }
  802.